package org.yelongframework.zip;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.yelongframework.commons.io.FileNameUtil;

/**
 * 压缩文件项集合
 * 
 * @since 1.0.0
 */
public class ZipFileEntrys {

	/** 压缩文件项 */
	private final List<ZipFileEntry> entries = new ArrayList<>();

	// ==================================================constructor==================================================

	public ZipFileEntrys() {
	}

	// ==================================================添加文件夹==================================================

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的名称。这是一个空目录
	 * 
	 * @param file 被压缩的文件
	 * @throws IOException 流异常
	 */
	public void addDirectorySimple(String name) throws IOException {
		add(new ZipFileEntry(name).setFileTypeDirectory());
	}

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的名称
	 * 
	 * @param file 被压缩的文件
	 * @throws IOException 流异常
	 */
	public void addDirectorySimple(File file) throws IOException {
		addDirectory(file.getName(), file);
	}

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的全路径名称
	 * 
	 * @param file 被压缩的文件
	 * @throws IOException 流异常
	 */
	public void addDirectoryFullPath(File file) throws IOException {
		addDirectory(file.getAbsolutePath(), file);
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name 在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param file 被压缩的文件
	 * @throws IOException 流异常
	 */
	public void addDirectory(String name, File file) throws IOException {
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
		if (file.isFile()) {
			throw new IOException("添加文件夹类型的压缩项时，该File实例不是文件夹");
		}
		if (name.endsWith("\\") || name.endsWith("/")) {
			name = name.substring(0, name.length() - 1);
		}
		Collection<File> listFiles = FileUtils.listFiles(file, null, true);
		for (File childFile : listFiles) {
			if (childFile.isDirectory()) {
				continue;
			}
			String absolutePath = childFile.getAbsolutePath();
			absolutePath = absolutePath.substring(file.getAbsolutePath().length() + 1);
			String childName = FileNameUtil.getFilePath(name, absolutePath);
			addFile(childName, childFile);
		}
	}

	// ==================================================添加文件==================================================

	/**
	 * 简单的添加一个压缩文件项。在压缩包内的文件名称就是文件的名称
	 * 
	 * @param file 被压缩的文件
	 */
	public void addFileSimple(File file) {
		addFile(file.getName(), file);
	}

	/**
	 * 简单的添加一个压缩文件项。在压缩包内的文件名称就是文件的全路径名称
	 * 
	 * @param file 被压缩的文件
	 */
	public void addFileFullPath(File file) {
		addFile(file.getAbsolutePath(), file);
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name 在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param file 被压缩的文件
	 */
	public void addFile(String name, File file) {
		addFile(name, () -> {
			if (!file.exists()) {
				throw new FileNotFoundException(file.getAbsolutePath());
			}
			if (file.isDirectory()) {
				throw new IOException("添加文件类型的压缩项时，该File实例不是文件");
			}
			return FileUtils.readFileToByteArray(file);
		});
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name        在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param inputStream 被压缩的文件输入流
	 */
	public void addFile(String name, InputStream inputStream) {
		addFile(name, () -> IOUtils.toByteArray(inputStream));
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name         在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param dataSupplier 数据供应商
	 */
	public void addFile(String name, ZipFileEntryDataSupplier dataSupplier) {
		add(new ZipFileEntry(name, dataSupplier).setFileTypeFile());
	}

	// ==================================================添加压缩文件项==================================================

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param entry 压缩文件项
	 */
	public void add(ZipFileEntry entry) {
		Objects.requireNonNull(entry);
		entries.add(entry);
	}

	/**
	 * 添加多个压缩文件项
	 * 
	 * @param entrys 压缩文件项集合
	 */
	public void add(List<ZipFileEntry> entrys) {
		Objects.requireNonNull(entrys);
		this.entries.addAll(entrys);
	}

	// ==================================================判断==================================================

	/**
	 * 压缩的文件项是否为空
	 * 
	 * @return <code>true</code> 不存在压缩的文件项
	 */
	public boolean isEmpty() {
		return entries.isEmpty();
	}

	// ==================================================获取压缩文件项==================================================

	/**
	 * @return 压缩文件项集合
	 */
	public List<ZipFileEntry> getEntries() {
		return entries;
	}

}
